import 'dart:math';

import 'package:flutter/material.dart';

class MyPainter extends CustomPainter {
  List<double> fft;

  MyPainter(this.fft);

  List<double> FREQUENCY_BAND_LIMITS = [
    20,
    25,
    32,
    40,
    50,
    63,
    80,
    100,
    125,
    160,
    200,
    250,
    315,
    400,
    500,
    630,
    800,
    1000,
    1250,
    1600,
    2000,
    2500,
    3150,
    4000,
    5000,
    6300,
    8000,
    10000,
    12500,
    16000,
    20000
  ];

  late int bands;

  int smoothingFactor = 3;
  late List<double> previousValues;
  int maxConst = 25000;

  double? leftX;

  double? top;

  double? rightX;

  double? height;

  @override
  void paint(Canvas canvas, Size size) {
    bands = FREQUENCY_BAND_LIMITS.length;
    previousValues = List<double>.filled(bands * smoothingFactor, 0.0);

    fftHandle(canvas, size);
  }

  void fftHandle(Canvas canvas, Size size) {
    final paintBandsFill = Paint()..color = Colors.blue;
    paintBandsFill.style = PaintingStyle.fill;

    int currentFftPosition = 0;
    int currentFrequencyBandLimitIndex = 0;
    double currentAverage = 0;

    while (currentFftPosition < fft.length) {
      double accum = 0;

      // Calculate the next limit index for the current frequency band
      int nextLimitAtPosition =
          ((FREQUENCY_BAND_LIMITS[currentFrequencyBandLimitIndex] / 20000) *
                  fft.length)
              .floor()
              .toInt();

      // Iterate within the current frequency band
      for (int j = 0; j < (nextLimitAtPosition - currentFftPosition); j += 2) {
        // Convert real and imaginary part to get energy
        num raw = (pow(fft[currentFftPosition + j], 2) +
            pow(fft[currentFftPosition + j + 1], 2));

        // Hamming window (by frequency band instead of frequency)
        int m = bands ~/ 2;
        double windowed = raw *
            (0.54 -
                0.46 * cos(2 * pi * currentFrequencyBandLimitIndex / (m + 1)));
        accum += windowed;
      }

      // Avoid division by zero
      if (nextLimitAtPosition - currentFftPosition != 0) {
        accum /= (nextLimitAtPosition - currentFftPosition);
      } else {
        accum = 0.0;
      }

      currentFftPosition = nextLimitAtPosition;

      // Smoothing
      double smoothedAccum = accum;
      for (int i = 0; i < smoothingFactor; i++) {
        smoothedAccum +=
            previousValues[i * bands + currentFrequencyBandLimitIndex];
        if (i != smoothingFactor - 1) {
          previousValues[i * bands + currentFrequencyBandLimitIndex] =
              previousValues[(i + 1) * bands + currentFrequencyBandLimitIndex];
        } else {
          previousValues[i * bands + currentFrequencyBandLimitIndex] = accum;
        }
      }
      smoothedAccum /= (smoothingFactor + 1);

      // Update current average
      currentAverage += smoothedAccum / bands;

      leftX = size.width * (currentFrequencyBandLimitIndex / bands.toDouble());
      rightX = leftX! + size.width / bands.toDouble();
      height =
          (size.height * ((smoothedAccum / maxConst).clamp(0, 1)).toDouble());
      top = size.height - height!;

      currentFrequencyBandLimitIndex++;

      canvas.drawRect(Rect.fromLTWH(leftX!, top!, rightX! - leftX!, height!),
          paintBandsFill);
    }
    // canvas.drawPath(fftPath, paintPath);
    //
    // canvas.drawLine(
    //     Offset(0,
    //         size.height * (1 - (currentAverage / maxConst))),
    //     Offset(size.width,
    //         size.height * (1 - (currentAverage / maxConst))),
    //     paintAvg
    // );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}
